#ifndef __SHAREMEM_HH_
#define __SHAREMEM_HH_

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <math.h>
#include <error.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include "mylog.h"

/**********************************************
 *          Eroor code
 *********************************************/
#define SHM_SUCCESS                             ( 0  ) //success
#define SHM_FAIL                                ( -1 ) //fail
#define SHM_PARAM                               ( -2 ) //param invalid
#define SHM_OVERFLOW                            ( -3 ) //overflow
#define SHM_RMID                                ( -4 ) //share memory has been delete

/**********************************************
 *        Eroor code end
 *********************************************/

#define MAX_CHN_NUM                             ( 3  ) //max chn num

typedef enum
{
    PT_PCMU          = 0,
    PT_1016          = 1,
    PT_G721          = 2,
    PT_GSM           = 3,
    PT_G723          = 4,
    PT_DVI4_8K       = 5,
    PT_DVI4_16K      = 6,
    PT_LPC           = 7,
    PT_PCMA          = 8,
    PT_G722          = 9,
    PT_S16BE_STEREO  = 10,
    PT_S16BE_MONO    = 11,
    PT_QCELP         = 12,
    PT_CN            = 13,
    PT_MPEGAUDIO     = 14,
    PT_G728          = 15,
    PT_DVI4_3        = 16,
    PT_DVI4_4        = 17,
    PT_G729          = 18,
    PT_G711A         = 19,
    PT_G711U         = 20,
    PT_G726          = 21,
    PT_G729A         = 22,
    PT_LPCM          = 23,
    PT_CelB          = 25,
    PT_JPEG          = 26,
    PT_CUSM          = 27,
    PT_NV            = 28,
    PT_PICW          = 29,
    PT_CPV           = 30,
    PT_H261          = 31,
    PT_MPEGVIDEO     = 32,
    PT_MPEG2TS       = 33,
    PT_H263          = 34,
    PT_SPEG          = 35,
    PT_MPEG2VIDEO    = 36,
    PT_AAC           = 37,
    PT_WMA9STD       = 38,
    PT_HEAAC         = 39,
    PT_PCM_VOICE     = 40,
    PT_PCM_AUDIO     = 41,
    PT_AACLC         = 42,
    PT_MP3           = 43,
    PT_ADPCMA        = 49,
    PT_AEC           = 50,
    PT_X_LD          = 95,
    PT_H264          = 96,
    PT_D_GSM_HR      = 200,
    PT_D_GSM_EFR     = 201,
    PT_D_L8          = 202,
    PT_D_RED         = 203,
    PT_D_VDVI        = 204,
    PT_D_BT656       = 220,
    PT_D_H263_1998   = 221,
    PT_D_MP1S        = 222,
    PT_D_MP2P        = 223,
    PT_D_BMPEG       = 224,
    PT_MP4VIDEO      = 230,
    PT_MP4AUDIO      = 237,
    PT_VC1           = 238,
    PT_JVC_ASF       = 255,
    PT_D_AVI         = 256,
    PT_DIVX3		 = 257,
    PT_AVS			 = 258,
    PT_REAL8		 = 259,
    PT_REAL9		 = 260,
    PT_VP6			 = 261,
    PT_VP6F			 = 262,
    PT_VP6A			 = 263,
    PT_SORENSON	 	 = 264,
    PT_H265          = 265,
    PT_MAX           = 266,
    /* add by hisilicon */
    PT_AMR           = 1001,
    PT_MJPEG         = 1002,
    PT_AMRWB         = 1003,
    PT_BUTT          = 5000
}PAYLOAD_TYPE_E;

typedef struct{
#ifdef RWLOCK
    pthread_rwlock_t mutex ;         // 共享内存互斥访问锁
#endif
    int64_t writePtr;             // 写位置的偏移
    int64_t lastWritePtr;         // 上一次写位置的偏移
    int64_t readPtr[MAX_CHN_NUM]; // 读位置的偏移(如果有多个读线程，这是最后一个的位置)
    int64_t memSize;              // sheremem的大小(包括本结构体)
}MEM_HEAD;

typedef struct{
    PAYLOAD_TYPE_E mediaType;       // 媒体类型
    int64_t tv;                     // 当前帧的时间信息
    int64_t lastFrame;            // 上一个的位置（如果kyeFrame=true,存储上一个关键帧的位置，否则存储最近一个frameNum的位置）
    int64_t nextFrame;            // 下一帧的位置
    bool keyFrame;                  // true : vedio 关键帧 audio 每秒第一帧
    int  frameNum;                  // keyFrame=true，frame=0，逐帧递增
    bool EndFrame;                  // true : 关键帧后面跟的最后一个普通帧
    int64_t lastKeyFrame;         // 上一个关键帧的位置（如果kyeFrame=true,存储上一个关键帧的位置，否则存储最近一个frameNum的位置）
    int64_t nextKeyFrame;         // 只在keyFrame=true时有效，下一个关键帧的位置
    int   mediaLen;                 // 帧长度
    char   mediaData[1];            // 帧起始地址
}FRAME_HEAD;

typedef struct{
    PAYLOAD_TYPE_E mediaType;       // 媒体类型
    int64_t tv;                     // 当前帧的时间信息
    bool keyFrame;                  // true : vedio 关键帧 audio 每秒第一帧
    int  frameNum;                  // keyFrame=true，frame=0，逐帧递增
    bool EndFrame;                  // true : 关键帧后面跟的最后一个普通帧
    int  mediaLen;                 // 帧长度
    char *mediaData;            // 帧起始地址
}FRAME_INFO;

#define D_MEM_HEAD                  ( (MEM_HEAD*)shareMemAdd )
#define D_FRAME_HEAD(offset)        ( (FRAME_HEAD*)(shareMemAdd+offset) )
#define D_MIN(a,b)                  ( a>b?b:a )
#define D_MAX(a,b)                  ( a>b?a:b )

class Sharemem
{
public:
    Sharemem( int shareId, int64_t memSize );
    Sharemem( int shareId, int chnNum );
    virtual ~Sharemem();

    Sharemem* createNew(int shareId, int64_t memSize );
    Sharemem* createNew(int shareId, int chnNum );
    int PushMedia( char *data, int len,struct timeval tvl = { 0, 0} );
    int PushMedia( char *data, int len, int64_t tv_ms, PAYLOAD_TYPE_E mediaType=PT_BUTT );
    int GetMedia( int chn, FRAME_INFO &media_info );
    int ReleaseMedia( int chn );
    int PopMedia( int chn, char *data, int &len, int64_t &tv_ms );
    int PopMedia( int chn, char *data, int &len );
    char* PopMedia( int chn, int &len, int64_t &tv_ms );
    char* PopMedia( int chn, int &len );

protected:
    virtual void moveReadPtr();
    virtual void getMediaInfo( const char *data, PAYLOAD_TYPE_E &mediaType, bool &keyFrame );

    bool checkAdd();
    int64_t getMinReadPtr();
    inline int64_t getFreeSize();                        //Current position to tail
    inline int64_t getNextWriteAdd( int len );
    void writePtrMoveToHead();
    void readPtrMoveToNextFrame( void );
    void readPtrMoveToNextKeyFrame( void );
    void overFlowCheckAndHandle( int32_t needlen );

protected:
    pthread_rwlock_t *rwlock;
    bool createMem = false;      //if Sharemem(int shareId, long long memSize ) true
    const char *fname = "/home";    // ftok fname
    int shareKeyId = 0;          // ftok id
    key_t shmKey = -1;           // shmKey = ftok( fname, shareMemId );
    int shmId = -1;              // shmId = shmget( fname, shareMemId );
    char *readPtr = NULL;        // read pointer[readChnNum]
    int readChnNum = 0;          // read chn num
    char *shareMemAdd = NULL;
    int64_t shareMemSize = -1; // share mem size
};

#endif
